package com.bluedot.www.framework.ibatis.mybaits.executor;


import com.bluedot.www.framework.ibatis.exceptions.MybatisException;
import com.bluedot.www.framework.ibatis.mybaits.builder.xml.XMLConfigBuilder;
import com.bluedot.www.framework.ibatis.mybaits.confiuration.Configuration;
import com.bluedot.www.framework.ibatis.mybaits.confiuration.Mapper;
import com.bluedot.www.framework.ibatis.mybaits.datasource.unpooled.UnpooledDataSourceFactory;
import com.bluedot.www.framework.ibatis.mybaits.io.Resources;
import com.bluedot.www.framework.ibatis.utils.SqlUtil;
import com.bluedot.www.framework.ibatis.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @author baiye
 * @date 2021/07/13/17:08
 */
public class SimpleExecutor implements Executor {
    private final Configuration configuration;
    Logger logger = LoggerFactory.getLogger(SqlUtil.class);

    public SimpleExecutor(Configuration configuration) {
        this.configuration = configuration;
    }

    public SimpleExecutor() {
        this("SqlMapConfig.xml");
    }

    public SimpleExecutor(String resourceName) {
        InputStream in = null;
        try {
            in = Resources.getResourceAsStream(resourceName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        configuration = XMLConfigBuilder.loadConfiguration(in);
    }

    @Override
    public <E> List<E> query(Mapper mapper, Object[] object) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        if (mapper == null) {
            throw new MybatisException("Mapper 为空，请注意检查是否配置了mapper");
        }
        String originalSql = mapper.getSql();
        String resultType = mapper.getResultType();
        String parameterType = mapper.getParameterType();

        String resultSql = null;
        Connection conn = null;
        logger.debug("originalSql...{}", originalSql);
        try {
            resultSql = SqlUtil.paramToSql(originalSql, object);
            logger.debug("resultSql...{}", resultSql);
            if ("UNPOOLED".equals(configuration.getDataSourceType())) {
                UnpooledDataSourceFactory unpooledDataSourceFactory = new UnpooledDataSourceFactory(configuration);
                conn = unpooledDataSourceFactory.getDataSource().getConnection();
                // conn = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
            } else if ("POOLED".equals(configuration.getDataSourceType())) {
                conn = configuration.getEnvironment().getDataSource().getConnection();
            }
            List list = new ArrayList<>();
            preparedStatement = conn.prepareStatement(resultSql);
            Class pojoClass = Class.forName(resultType);
            resultSet = preparedStatement.executeQuery();
            if (resultType.equals("java.lang.Integer")) {
                if (resultSet.next()) {
                    list.add(resultSet.getInt(1));
                    return list;

                }
            } else if ("java.lang.Long".equals(resultType)) {
                int count = 1;
                // count 表示列数，不需要变化
                while (resultSet.next()) {
                    list.add(resultSet.getLong(count));
                }
                return list;
            }

            while (resultSet.next()) {
                E pojo = (E) pojoClass.newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    columnName = StringUtil.underlineToHump(columnName);

                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, pojoClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(pojo, value);
                }
                list.add(pojo);
            }
            return list;
        } catch (InvocationTargetException | IllegalAccessException | IntrospectionException | InstantiationException e) {
            throw new MybatisException("执行query时注入实体类错误", e);
        } catch (SQLException e) {
            throw new MybatisException("SQL语法错误: " + e.getMessage(), e);
        } catch (ClassNotFoundException e) {
            throw new MybatisException(resultType + " 不存在: " + e.getMessage(), e);
        } catch(Exception e) {
            throw new MybatisException("JDBC返回类型与JavaBean属性接受类型不一致或执行query时注入实体类错误");
        } finally {
            release(conn, preparedStatement, resultSet);
        }
    }

    public <E> List<E> query(String mapperName, Object[] object) {
        Mapper mapper = configuration.getMappers().get(mapperName);//com.bluedot.www.core.mapper.xml.UserMapper.isExistEmail
        return query(mapper, object);
    }

    @Override
    public void close() {

    }

    @Override
    public int update(Mapper mapper, Object[] object) {
        if (mapper == null) {
            throw new MybatisException("mapper 为空，请注意是否配置了该xml文件");
        }
        PreparedStatement preparedStatement = null;
        //xml里的sql字符串
        String originalSql = mapper.getSql();
        String resultSql = null;
        String parameterType = mapper.getParameterType();
        Connection conn = null;
        logger.info("originalSql...{}", originalSql);
        try {
            resultSql = SqlUtil.paramToSql(originalSql, object);
            logger.info("resultSql...{}", resultSql);
            if ("UNPOOLED".equals(configuration.getDataSourceType())) {
                UnpooledDataSourceFactory unpooledDataSourceFactory = new UnpooledDataSourceFactory(configuration);
                conn = unpooledDataSourceFactory.getDataSource().getConnection();
                // conn = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
            } else if ("POOLED".equals(configuration.getDataSourceType())) {
                conn = configuration.getEnvironment().getDataSource().getConnection();
            }
            //conn = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
            preparedStatement = conn.prepareStatement(resultSql);
            return preparedStatement.executeUpdate();
        } catch (SQLException  e) {
            e.printStackTrace();
            throw new MybatisException("update时sql执行或注入实体类错误", e);
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            release(conn, preparedStatement, null);
        }
        return 0;
    }

    public int update(String mapperName, Object[] object) {
        Mapper mapper = configuration.getMappers().get(mapperName);
        return update(mapper, object);
    }

    private void release(Connection con, PreparedStatement pstm, ResultSet rs) {
        if (con != null) {
            try {
                con.close();
            } catch (Exception e) {
                throw new MybatisException("资源释放异常", e);
            }
        }
        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                throw new MybatisException("资源释放异常", e);
            }
        }

        if (pstm != null) {
            try {
                pstm.close();
            } catch (Exception e) {
                throw new MybatisException("资源释放异常", e);
            }
        }
    }


}
